﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AdaptiveDeNet.RAND;

namespace AdaptiveDeNet.DifferentialEvolution.Common.Crossover
{
    public class rand1exp : IStrategy
    {
        private double F;
        private double Cr;

        private int dim;
        private int N;

        private double[] lowerBound;
        private double[] upperBound;

        private RAND.Rand nRand;

        /// <summary>
        /// Initializes all necessary tributes for rand/1/bin strategy
        /// </summary>
        /// <param name="_F">F</param>
        /// <param name="_Cr">Cr</param>
        /// <param name="_dim">dimension</param>
        /// <param name="_N">count of members in population</param>
        /// <param name="_lowerBound">lower bound dimension</param>
        /// <param name="_upperBound">upper bound dimension</param>
        /// <param name="_nRand">random number generator</param>
        public void InitParams(double _F, double _Cr, int _dim, int _N, double[] _lowerBound, double[] _upperBound, RAND.Rand _nRand)
        {
            this.F = _F;
            this.Cr = _Cr;
            this.dim = _dim;
            this.N = _N;
            this.lowerBound = _lowerBound;
            this.upperBound = _upperBound;

            this.nRand = _nRand;
        }

        public Member GetTrial(Member[] mainPop, int targetIndex)
        {
            Member trial = new Member(this.dim);

            // 3 indexy
            int[] r = GetRIndices(this.N, targetIndex);
            int jrand = this.nRand.Next(this.dim);

            int jrandTemp = jrand;

            double ret = double.MaxValue;
            int indexBest = 0;

            for (int i = 0; i < r.Length; i++)
            {
                if (ret > mainPop[r[i]].FEval)
                {
                    ret = mainPop[r[i]].FEval;
                    indexBest = i;
                }
            }

            int temp = r[indexBest];
            r[indexBest] = r[0];
            r[0] = temp;

            do
            {
                // 
                // x = r0 + F * (r1 - r2) - mutant
                double newVal = mainPop[r[0]].Vector[jrandTemp] + F * (mainPop[r[1]].Vector[jrandTemp] - mainPop[r[2]].Vector[jrandTemp]);

                //zrdcadleni
                if (newVal < lowerBound[jrandTemp] || newVal > upperBound[jrandTemp])
                {
                    //newVal = nRand.NextDouble() * (upperBound[jrandTemp] - lowerBound[jrandTemp]) + lowerBound[jrandTemp]; 
                    newVal = Common.Zrdcad(newVal, lowerBound[jrandTemp], upperBound[jrandTemp]);
                }
                trial.Vector[jrandTemp] = newVal;

                ++jrandTemp; //increment

            } while (nRand.NextDouble() <= Cr && jrandTemp < dim);

            //pokud rand <= Cr pretece populaci
            if (jrandTemp == dim - 1)
            {
                jrandTemp = 0;

                do
                {
                    // 
                    // x = r0 + F * (r1 - r2) - mutant
                    double newVal = mainPop[r[0]].Vector[jrandTemp] + F * (mainPop[r[1]].Vector[jrandTemp] -
                        mainPop[r[2]].Vector[jrandTemp]);

                    //zrdcadleni
                    if (newVal < lowerBound[jrandTemp] || newVal > upperBound[jrandTemp])
                    {
                        //newVal = nRand.NextDouble() * (upperBound[jrandTemp] - lowerBound[jrandTemp]) + lowerBound[jrandTemp];
                        newVal = Common.Zrdcad(newVal, lowerBound[jrandTemp], upperBound[jrandTemp]);
                    }
                    trial.Vector[jrandTemp] = newVal;

                    ++jrandTemp; //increment  

                } while (nRand.NextDouble() <= Cr && jrandTemp < jrand);

                if (jrandTemp < jrand)
                {
                    for (int j = jrandTemp; j < jrand; j++)
                    {
                        trial.Vector[j] = mainPop[targetIndex].Vector[j];
                    }
                }
            }
            else //jinak dokopirovani
            {
                for (int j = jrandTemp; j < trial.Vector.Length; j++)
                {
                    trial.Vector[j] = mainPop[targetIndex].Vector[j];
                }

                for (int j = 0; j < jrand; j++)
                {
                    trial.Vector[j] = mainPop[targetIndex].Vector[j];
                }
            }

            return trial;
        }

        public Member GetTrial(Member[] mainPop, int targetIndex, RAND.RngStream rngStream)
        {
            Member trial = new Member(dim);

            int n1 = N - 1;

            int r0Index = rngStream.randInt(0, n1);
            int r1Index = rngStream.randInt(0, n1);
            int r2Index = rngStream.randInt(0, n1);

            while (targetIndex == r0Index) r0Index = rngStream.randInt(0, n1);

            while (targetIndex == r1Index || r0Index == r1Index) r1Index = rngStream.randInt(0, n1);

            while (targetIndex == r2Index || r0Index == r2Index || r1Index == r2Index) r2Index = rngStream.randInt(0, n1);

            int jrand = rngStream.randInt(0, dim - 1);
            int jrandTemp = jrand;

            int pom = 0;

            //random location
            if (mainPop[r2Index].FEval > mainPop[r1Index].FEval)
            {
                if (mainPop[r1Index].FEval < mainPop[r0Index].FEval)
                {
                    pom = r0Index;
                    r0Index = r1Index;
                    r1Index = pom;
                }
            }
            else
            {
                if (mainPop[r2Index].FEval < mainPop[r0Index].FEval)
                {
                    pom = r0Index;
                    r0Index = r2Index;
                    r2Index = pom;
                }
            }

            do
            {
                //mutace
                // x = r0 + F * (r1 - r2) - mutant
                double newVal = mainPop[r0Index].Vector[jrandTemp] + F * (mainPop[r1Index].Vector[jrandTemp] - mainPop[r2Index].Vector[jrandTemp]);

                //zrdcadleni
                if (newVal < lowerBound[jrandTemp] || newVal > upperBound[jrandTemp])
                {
                    //newVal = nRand.NextDouble() * (upperBound[jrandTemp] - lowerBound[jrandTemp]) + lowerBound[jrandTemp]; 
                    newVal = Common.Zrdcad(newVal, lowerBound[jrandTemp], upperBound[jrandTemp]);
                }
                trial.Vector[jrandTemp] = newVal;

                ++jrandTemp; //increment

            } while (rngStream.randU01() <= Cr && jrandTemp < dim);

            //pokud rand <= Cr pretece populaci
            if (jrandTemp == dim - 1)
            {
                jrandTemp = 0;

                do
                {
                    //mutace
                    // x = r0 + F * (r1 - r2) - mutant
                    double newVal = mainPop[r0Index].Vector[jrandTemp] + F * (mainPop[r1Index].Vector[jrandTemp] -
                        mainPop[r2Index].Vector[jrandTemp]);

                    //zrdcadleni
                    if (newVal < lowerBound[jrandTemp] || newVal > upperBound[jrandTemp])
                    {
                        //newVal = nRand.NextDouble() * (upperBound[jrandTemp] - lowerBound[jrandTemp]) + lowerBound[jrandTemp];
                        newVal = Common.Zrdcad(newVal, lowerBound[jrandTemp], upperBound[jrandTemp]);
                    }
                    trial.Vector[jrandTemp] = newVal;

                    ++jrandTemp; //increment  

                } while (rngStream.randU01() <= Cr && jrandTemp < jrand);

                if (jrandTemp < jrand)
                {
                    for (int j = jrandTemp; j < jrand; j++)
                    {
                        trial.Vector[j] = mainPop[targetIndex].Vector[j];
                    }
                }
            }
            else //jinak dokopirovani
            {
                for (int j = jrandTemp; j < trial.Vector.Length; j++)
                {
                    trial.Vector[j] = mainPop[targetIndex].Vector[j];
                }

                for (int j = 0; j < jrand; j++)
                {
                    trial.Vector[j] = mainPop[targetIndex].Vector[j];
                }
            }

            return trial;
        }

        private int[] GetRIndices(int N, int targetIndex)
        {
            int[] ret = new int[3];

            int r0Index = this.nRand.Next(N);
            int r1Index = this.nRand.Next(N);
            int r2Index = this.nRand.Next(N);

            while (targetIndex == r0Index) r0Index = nRand.Next(N);

            while (targetIndex == r1Index || r0Index == r1Index) r1Index = nRand.Next(N);

            while (targetIndex == r2Index || r0Index == r2Index || r1Index == r2Index) r2Index = nRand.Next(N);

            ret[0] = r0Index;
            ret[1] = r1Index;
            ret[2] = r2Index;

            return ret;
        }
    }
}
